package com.coolingme.springframework.context;

import com.coolingme.springframework.aop.MeDefaultAopProxyFactory;
import com.coolingme.springframework.aop.config.MeAopConfig;
import com.coolingme.springframework.aop.support.MeAdvisedSupport;
import com.coolingme.springframework.beans.MeBeanWrapper;
import com.coolingme.springframework.beans.config.MeBeanDefinition;
import com.coolingme.springframework.context.support.MeBeanDefinitionReader;
import com.coolingme.springframework.context.support.MeDefaultListableBeanFactory;
import com.coolingme.springframework.core.MeBeanFactory;
import com.coolingme.springframework.annotation.MeAutowired;
import com.coolingme.springframework.annotation.MeController;
import com.coolingme.springframework.annotation.MeService;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 简单理解为工厂类
 * 完成Bean的创建和DI
 *
 * @author wangyue
 * @date 2020/11/10 15:24
 */
public class MeApplicationContext implements MeBeanFactory {

    /**
     * 在AbstractRefreshableApplicationContext中静态代理了DefaultListableBeanFactory
     * DefaultListableBeanFactory beanFactory = this.beanFactory;
     */
    private MeDefaultListableBeanFactory registry = new MeDefaultListableBeanFactory();

    /**
     * AOP代理工厂，用于创建代理
     */
    private MeDefaultAopProxyFactory proxyFactory = new MeDefaultAopProxyFactory();

    /**
     * 配置文件读取对象
     */
    private MeBeanDefinitionReader reader;

    /**
     * 循环依赖的标识，当前正在创建的BeanName，标记一下
     */
    private Set<String> singletonsCurrentlyInCreation = new HashSet<>();

    /**
     * 一级缓存，所有成熟的Bean
     * key: 接口全类名、beanName类名小写
     * value：类实例对象
     */
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    /**
     * 二级缓存，原生的早期Bean
     */
    private Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();

    /**
     * 三级缓存（终极缓存），保存代理的Bean
     * <p>
     * 通用的IOC容器缓存，保存所有Bean对应的实例信息（接口和实现），给DI使用
     * key ：factoryBeanName（bean名称），类名小写，接口为全类名
     * value：BeanWrapper类对象
     */
    private Map<String, MeBeanWrapper> factoryBeanInstancesCache = new ConcurrentHashMap<>();

    /**
     * 三级缓存（终极缓存），代理的类也会放在里面
     * <p>
     * 单例的IOC容器缓存，注册式单例模式
     * key ：接口全类名、实现类全类名、beanName类名小写
     * value：类实例对象
     * <p>
     * "com.coolingme.demo.service.impl.DemoServiceImpl" -> {DemoServiceImpl@5132}
     * "com.coolingme.demo.controller.DemoController" -> {DemoController@5134}
     */
    private Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>();


    public MeApplicationContext(String... configLocations) {
        try {
            // 1.1. 加载配置文件及扫描相关的类
            reader = new MeBeanDefinitionReader(configLocations);

            // 1.2. 解析配置文件，并将相关的类封装成BeanDefinition
            List<MeBeanDefinition> beanDefinitions = reader.loadBeanDefinition();
            System.out.println("beanDefinitions");
            for (MeBeanDefinition beanDefinition : beanDefinitions) {
                System.out.println(beanDefinition);
            }


            // 1.3. 把BeanDefinition缓存起来，把beanDefinition的factoryBeanName作为key，beanDefinition作为value
            this.registry.doRegisterBeanDefinition(beanDefinitions);

            // 1.4. 加载非延时加载的所有的Bean
            doLoadInstance();

            System.out.println("ApplicationContext is init.");

        } catch (Exception e) {
            System.err.println("ApplicationContext init fail.");
            e.printStackTrace();
        }
    }


    /**
     * 注入Bean对象
     * 延时加载时被getBean()触发
     */
    private void doLoadInstance() {
        for (Map.Entry<String, MeBeanDefinition> beanDefinitionEntry : this.registry.beanDefinitionMap.entrySet()) {
            // 延时加载就跳过
            if (beanDefinitionEntry.getValue().isLazyInit()) {
                continue;
            }
            String beanName = beanDefinitionEntry.getKey();
            getBean(beanName);
        }
    }


    /**
     * 获取Bean对象
     *
     * @param requiredType bean的类对象
     * @return Bean对象
     */
    @Override
    public <T> T getBean(Class<T> requiredType) {
        return (T) this.getBean(requiredType.getName());
    }

    /**
     * 获取Bean对象
     * 依赖注入，从这里开始，通过读取 BeanDefinition 中的信息
     * 然后，通过反射机制创建一个实例并返回
     * Spring做法是，不会把最原始的对象放出去，会用一个 Beanwrapper来进行一次包装
     * 装饰器模式∶
     * 1、保留原来的0OP关系
     * 2、我需要对它进行扩展，增强（为了以后 AOP打基础）
     *
     * @param beanName bean名称，类名小写，接口为全类名
     * @return Bean对象
     */
    @Override
    public Object getBean(String beanName) {
        // 1、获取bean配置信息
        MeBeanDefinition beanDefinition = this.registry.beanDefinitionMap.get(beanName);

        // 从缓存中获取单例Bean
        Object singleton = getSingleton(beanName, beanDefinition);

        if (singleton != null) {
            return singleton;
        }

        // 如果bean不存在，就标记为正在创建
        if (!singletonsCurrentlyInCreation.contains(beanName)) {
            singletonsCurrentlyInCreation.add(beanName);
        }

        // 2、实例化对象，把A放入到三级缓存中的
        Object instance = instantiateBean(beanName, beanDefinition);

        /*MeBeanWrapper beanWrapper;
        if (!this.factoryBeanInstancesCache.containsKey(beanName)) {

            // 3、把对象封装到BeanWrapper中，先判断factoryBeanInstancesCache中是否存在，不存在再创建
            beanWrapper = new MeBeanWrapper(instance);

            // 4、把BeanWrapper存到IoC容器中
            this.factoryBeanInstancesCache.put(beanName, beanWrapper);
        }
        beanWrapper = this.factoryBeanInstancesCache.get(beanName);

        //5、执行依赖注入
        populateBean(beanName, beanWrapper);
        */

        //3、将返回的Bean的对象封装成BeanWrapper
        MeBeanWrapper beanWrapper = new MeBeanWrapper(instance);

        //4、执行依赖注入
        populateBean(beanName, beanDefinition, beanWrapper);

        // bean 创建完成，移除标记
        if (singletonsCurrentlyInCreation.contains(beanName)) {
            singletonsCurrentlyInCreation.remove(beanName);
        }

        // 把A放入到一级缓存中，
        this.singletonObjects.put(beanName, instance);

        return beanWrapper.getWrappedInstance();
    }

    private Object getSingleton(String beanName, MeBeanDefinition beanDefinition) {
        // 虽然我们最开始从A开始加载，然后注入属性B，然后B再去创建，属性注入A
        // 1. 先从一级缓存中获取，现在是B注入A
        Object bean = singletonObjects.get(beanName);
        // 一级缓存中没有，并且又有创建标识，说明就是循环依赖
        if (bean == null && singletonsCurrentlyInCreation.contains(beanName)) {

            // 2. 从二级缓存中获取
            bean = earlySingletonObjects.get(beanName);

            // 二级缓存没有，从三级缓存中拿
            if (bean == null) {
                bean = instantiateBean(beanName, beanDefinition);
            }

            // 把创建出来的bean放到二级缓存中
            earlySingletonObjects.put(beanName, bean);
        }
        return bean;
    }

    /**
     * 实例化对象，放入IoC容器中
     *
     * @param beanDefinition bean的配置信息
     * @return bean的实例
     */
    private Object instantiateBean(String beanName, MeBeanDefinition beanDefinition) {
        // 可能存在循环依赖，从三级缓存中拿
        if (beanDefinition.isSingleton() && this.factoryBeanObjectCache.containsKey(beanName)) {
            return this.factoryBeanObjectCache.get(beanName);
        }

        // 1、拿到要实例化的对象的类名
        String className = beanDefinition.getBeanClassName();

        // 2、反射实例化，得到一个对象
        Object instance = null;

        try {
            // 假设默认就是单例，暂时不考虑细节，先把主线拉通
            /*if (this.factoryBeanObjectCache.containsKey(className)) {
                instance = this.factoryBeanObjectCache.get(className);
            } else {
                // 获取class对象
                Class<?> clazz = Class.forName(className);
                // 通过class对象创建实例
                instance = clazz.newInstance();
                this.factoryBeanObjectCache.put(className, instance);
            }*/
            Class<?> clazz = Class.forName(className);
            instance = clazz.newInstance();

            //==================AOP动态代理开始=========================
            // 如果满足条件，就直接返回Proxy对象
            //1、加载AOP的配置文件
            MeAdvisedSupport config = instantionAopConfig(beanDefinition);
            // 设置目标对象Class
            // 并解析配置文件，建立目标对象关系的方法和切面通知的关系
            config.setTargetClass(clazz);
            // 设置目标对象实例
            config.setTarget(instance);

            // 判断规则，要不要生成代理类，如果要就覆盖原生对象
            // 如果不要就不做任何处理，返回原生对象
            if (config.pointCutMath()) {
                instance = proxyFactory.createAopProxy(config).getProxy();
            }
            //==================AOP动态代理结束=========================

            // 三级缓存，放入一个map里面
            this.factoryBeanObjectCache.put(beanName, instance);

            this.factoryBeanObjectCache.put(clazz.getName(), instance);
            for (Class<?> i : clazz.getInterfaces()) {
                this.factoryBeanObjectCache.put(i.getName(), instance);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    private MeAdvisedSupport instantionAopConfig(MeBeanDefinition beanDefinition) {
        MeAopConfig config = new MeAopConfig();
        config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
        config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
        config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
        config.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
        config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
        config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));
        return new MeAdvisedSupport(config);
    }

    /**
     * 执行依赖注入
     *
     * @param beanName    bean名称，类名小写，接口为全类名
     * @param beanWrapper Bean的包装
     */
    private void populateBean(String beanName, MeBeanDefinition beanDefinition, MeBeanWrapper beanWrapper) {
        //可能涉及到循环依赖？
        // A----------注入B ----创建B----B要注入A-----直接拿到了A---完成B的创建----->A完成B的注入-->放入容器
        //A{ B b}
        //B{ A b}
        //用两个缓存，循环两次
        //1、把第一次读取结果为空的BeanDefinition存到第一个缓存
        //2、等第一次循环之后，第二次循环再检查第一次的缓存，再进行赋值

        Object instance = beanWrapper.getWrappedInstance();
        Class<?> clazz = beanWrapper.getWrappedClass();

        // 只有加了注解才注入
        if (!(clazz.isAnnotationPresent(MeController.class) || clazz.isAnnotationPresent(MeService.class))) {
            return;
        }

        //把所有的包括private/protected/default/public 修饰字段都取出来
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 没有使用MeAutowired注释就不注入了
            if (!field.isAnnotationPresent(MeAutowired.class)) {
                continue;
            }

            // 暴力访问
            field.setAccessible(true);

            try {
                // 如果已经被注入了，就不需要再注入
                if (null != field.get(instance)) {
                    continue;
                }
            } catch (IllegalAccessException e) {
            }

            MeAutowired meAutowired = field.getAnnotation(MeAutowired.class);
            String autowiredBeanName = meAutowired.value().trim();

            // 如果没有定义beanName，就默认根据类型注入
            if ("".equals(autowiredBeanName)) {
                autowiredBeanName = field.getType().getName();
            }

            // 反射赋值
            try {
                //相当于 demoAction.demoService = ioc.get("com.gupaoedu.demo.service.IDemoService");
//                field.set(instance, this.factoryBeanInstancesCache.get(autowiredBeanName).getWrappedInstance());
//                System.out.println("DI " + beanName + " => " + field.getName() + " : " + this.factoryBeanInstancesCache.get(autowiredBeanName).getWrappedInstance());

                // 此处在A对象中注入B,autowiredBeanName这个就是B的名称，B创建完后，又回到了A属性注入的地方
                // 不再从缓存中获取，递归获取Bean
                Object autowiredBean = getBean(autowiredBeanName);
                field.set(instance, autowiredBean);
                System.out.println("DI " + beanName + " => " + field.getName() + " : " + autowiredBean);
            } catch (Exception e) {
                System.err.println("依赖注入失败");
                e.printStackTrace();
            }
        }
    }


    /**
     * 执行依赖注入
     *
     * @param beanName    bean名称，类名小写，接口为全类名
     * @param beanWrapper Bean的包装
     */
    private void populateBean(String beanName, MeBeanWrapper beanWrapper) {
        //可能涉及到循环依赖？
        // A----------注入B ----创建B----B要注入A-----直接拿到了A---完成B的创建----->A完成B的注入-->放入容器
        //A{ B b}
        //B{ A b}
        //用两个缓存，循环两次
        //1、把第一次读取结果为空的BeanDefinition存到第一个缓存
        //2、等第一次循环之后，第二次循环再检查第一次的缓存，再进行赋值

        Object instance = beanWrapper.getWrappedInstance();
        Class<?> clazz = beanWrapper.getWrappedClass();

        // 只有加了注解才注入
        if (!(clazz.isAnnotationPresent(MeController.class) || clazz.isAnnotationPresent(MeService.class))) {
            return;
        }

        //把所有的包括private/protected/default/public 修饰字段都取出来
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 没有使用MeAutowired注释就不注入了
            if (!field.isAnnotationPresent(MeAutowired.class)) {
                continue;
            }

            // 暴力访问
            field.setAccessible(true);

            try {
                // 如果已经被注入了，就不需要再注入
                if (null != field.get(instance)) {
                    continue;
                }
            } catch (IllegalAccessException e) {
            }

            MeAutowired meAutowired = field.getAnnotation(MeAutowired.class);
            String autowiredBeanName = meAutowired.value().trim();

            // 如果没有定义beanName，就默认根据类型注入
            if ("".equals(autowiredBeanName)) {
                autowiredBeanName = field.getType().getName();
            }

            // 反射赋值
            try {
                //相当于 demoAction.demoService = ioc.get("com.gupaoedu.demo.service.IDemoService");
//                field.set(instance, this.factoryBeanInstancesCache.get(autowiredBeanName).getWrappedInstance());
//                System.out.println("DI " + beanName + " => " + field.getName() + " : " + this.factoryBeanInstancesCache.get(autowiredBeanName).getWrappedInstance());

                // 此处在A对象中注入B,autowiredBeanName这个就是B的名称，B创建完后，又回到了A属性注入的地方
                // 不再从缓存中获取，递归获取Bean
                Object autowiredBean = getBean(autowiredBeanName);
                field.set(instance, autowiredBean);
                System.out.println("DI " + beanName + " => " + field.getName() + " : " + autowiredBean);
            } catch (Exception e) {
                System.err.println("依赖注入失败");
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取bean配置信息的数量
     *
     * @return bean配置信息的数量
     */
    public Integer getBeanDefinitionCount() {
        return this.registry.beanDefinitionMap.size();
    }

    /**
     * 获取beanDefinitionMap的key数组
     *
     * @return beanDefinitionMap的key数组
     */
    public String[] getBeanDefinitionNames() {
        return this.registry.beanDefinitionMap.keySet().toArray(new String[this.registry.beanDefinitionMap.size()]);
    }

    public Properties getConfig() {
        return this.reader.getConfig();
    }
}
